using System;
using System.Collections.Generic;
using System.Text;
using AgilePlannerDT.Data;
using System.ComponentModel;
using System.Threading;

namespace AgilePlannerDT.Data.Impl
{
    public class IterationDataObject : IndexCardWithChildDataObject, Iteration
    {
        #region Constructors

        public IterationDataObject()
            : this(0, 0, "Default Iteration", "no description", CardConstants.ITERATION_DEFAULT_WIDTH, CardConstants.ITERATION_DEFAULT_HEIGHT, CardConstants.INDEXCARD_DEFAULT_LOCATION_X, CardConstants.INDEXCARD_DEFAULT_LOCATION_Y, 0.0f, DateTime.Today, DateTime.Today, CardConstants.STATUS_DEFINED)
        {
            this.storycards = new List<StoryCard>();
        }

        public IterationDataObject(Iteration original)
        {
            this.actualEffort = original.ActualEffort;
            this.availableEffort = original.AvailableEffort;
            this.bestCaseEstimate = original.BestCaseEstimate;
            this.description = original.Description;
            this.enddate = original.EndDate;
            this.height = original.Height;
            this.id = original.ID;
            this.SyncID = original.SyncID;
            this.locationX = original.LocationX;
            this.locationY = original.LocationY;
            this.mostLikelyEstimate = original.MostlikelyEstimate;
            this.name = original.Name;
            this.parent = original.Parent;
            this.rotationAngle = original.RotationAngle;
            this.startdate = original.StartDate;
            this.status = original.Status;
            this.width = original.Width;
            this.worstCaseEstimate = original.WorstCaseEstimate;

            foreach (StoryCard sc in original.StoryCardChildren)
            {
                AddStoryCard(new StoryCardDataObject(sc));
            }
        }

        public IterationDataObject(long id, long parent, string name, string description, double width, double height, double locationX, double locationY, float availableEffort,
                DateTime startDate, DateTime endDate, string status)
        {
            this.actualEffort = 0;
            this.availableEffort = availableEffort;
            this.bestCaseEstimate = 0;
            this.description = description;
            this.enddate = endDate;
            this.height = height;
            this.id = id;
            this.locationX = locationX;
            this.locationY = locationY;
            this.mostLikelyEstimate = 0;
            this.name = name;
            this.parent = parent;
            this.rotationAngle = 0;
            this.startdate = startDate;
            this.status = status;
            this.storycards = new List<StoryCard>();
            this.width = width;
            this.worstCaseEstimate = 0;
        }

        public IterationDataObject(string name, string description, double width, double height, double locationX, double locationY, float availableEffort, DateTime startDate, DateTime endDate) :
            this(0, 0, name, description, width, height, locationX, locationY, availableEffort, startDate, endDate, CardConstants.STATUS_DEFINED)
        {
        }

        public IterationDataObject(long id, long parent)
            : this(id, parent, "Default Iteration", "no description", CardConstants.ITERATION_DEFAULT_WIDTH, CardConstants.ITERATION_DEFAULT_HEIGHT, CardConstants.INDEXCARD_DEFAULT_LOCATION_X, CardConstants.INDEXCARD_DEFAULT_LOCATION_Y, 0.0f, DateTime.Today, DateTime.Today, CardConstants.STATUS_DEFINED)
        {
        }

        #endregion

        #region IndexCardWithChild Members

        public new float RemainingEffort
        {
            get
            {
                float sumStoryCard = 0;

                foreach (StoryCard storycard in StoryCardChildren)
                {
                    sumStoryCard += storycard.MostlikelyEstimate;
                }

                return (availableEffort - sumStoryCard);
            }
        }

        #endregion

        #region Object Members

        public override string ToString()
        {
            string iteration = "###################################\n" + "#            Iteration            #\n"
                    + "###################################\n\n" + "ID:\t\t" + ID + "\n" + "Parent:\t\t" + Parent + "\n" + "Name:\t\t"
                    + Name + "\n" + "Description:\t" + Description + "\n" + "Size:\t\t" + Width + " x " + Height + "\n"
                    + "Location:\t(" + LocationX + " , " + LocationY + ")\n" + "Start date:\t" + StartDate + "\n" + "End date:\t"
                    + EndDate + "\n" + "Available:\t" + AvailableEffort + "\n" + "Status:\t\t" + Status + "\n\n";
            return iteration;
        }

        public override bool Equals(object o)
        {
            if (o is IterationDataObject)
            {
                IterationDataObject i = (IterationDataObject)o;

                if (i.StoryCardChildren.Count == StoryCardChildren.Count)
                {
                    bool children = true;

                    //maybe use Find here instead of just checking all storycards
                    for (int j = 0; j < StoryCardChildren.Count; j++)
                    {
                        children = children && i.StoryCardChildren[j].Equals(StoryCardChildren[j]);
                    }

                    return (children &&
                        i.AvailableEffort == AvailableEffort &&
                        i.Description.Equals(Description) &&
                        i.EndDate.Equals(EndDate) &&
                        i.Height == Height &&
                        i.ID == ID &&
                        i.Name.Equals(Name) &&
                        i.Parent == Parent &&
                        i.StartDate.Equals(StartDate) &&
                        i.Status.Equals(Status) &&
                        i.Width == Width &&
                        i.LocationX == LocationX &&
                        i.LocationY == LocationY);
                }
            }
            return false;
        }

        public override int GetHashCode()
        {
            return (int)id;
        }

        #endregion

        #region Iteration Members

        // DOES NOT UPDATE PARENT-CHILD RELATIONS!
        public void Update(Iteration iteration)
        {
            Iteration before = (Iteration)Clone();

            this.actualEffort = iteration.ActualEffort;
            this.availableEffort = iteration.AvailableEffort;
            this.bestCaseEstimate = iteration.BestCaseEstimate;
            this.description = iteration.Description;
            this.enddate = iteration.EndDate;
            this.height = iteration.Height;
            this.locationX = iteration.LocationX;
            this.locationY = iteration.LocationY;
            this.mostLikelyEstimate = iteration.MostlikelyEstimate;
            this.name = iteration.Name;
            this.rotationAngle = iteration.RotationAngle;
            this.startdate = iteration.StartDate;
            this.status = iteration.Status;
            this.width = iteration.Width;
            this.worstCaseEstimate = iteration.WorstCaseEstimate;

            Iteration after = (Iteration)Clone();

            //move and resize don't trigger notifies on change (they change to often for
            //firing on every update) so we have to check for a change here
            if (before.LocationX != after.LocationX ||
                before.LocationY != after.LocationY ||
                before.RotationAngle != after.RotationAngle)
            {
                Notify(POSITION_PROPERTY, before, after);
            }
            else if (before.Width != after.Width || before.Height != after.Height)
            {
                Notify(SIZE_PROPERTY, before, after);
            }
        }

        public void UpdateWithoutNotifyChanges(Iteration iteration)
        {
            this.actualEffort = iteration.ActualEffort;
            this.availableEffort = iteration.AvailableEffort;
            this.bestCaseEstimate = iteration.BestCaseEstimate;
            this.description = iteration.Description;
            this.enddate = iteration.EndDate;
            this.height = iteration.Height;
            this.locationX = iteration.LocationX;
            this.locationY = iteration.LocationY;
            this.mostLikelyEstimate = iteration.MostlikelyEstimate;
            this.name = iteration.Name;
            this.rotationAngle = iteration.RotationAngle;
            this.startdate = iteration.StartDate;
            this.status = iteration.Status;
            this.width = iteration.Width;
            this.worstCaseEstimate = iteration.WorstCaseEstimate;
        }

        #endregion

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            AvailableEffort = float.Parse(reader.GetAttribute("AvailableEffort"));
            Description = reader.GetAttribute("Description");
            EndDate = DateTime.ParseExact(reader.GetAttribute("EndDate"), "yyyy-MM-dd HH:mm:ss.FFF", Thread.CurrentThread.CurrentCulture);
            Height = double.Parse(reader.GetAttribute("Height"));
            ID = long.Parse(reader.GetAttribute("ID"));
            SyncID = int.Parse(reader.GetAttribute("SyncID"));
            Name = reader.GetAttribute("Name");
            Parent = long.Parse(reader.GetAttribute("Parent"));
            StartDate = DateTime.ParseExact(reader.GetAttribute("StartDate"), "yyyy-MM-dd HH:mm:ss.FFF", Thread.CurrentThread.CurrentCulture);
            Status = reader.GetAttribute("Status");
            Width = double.Parse(reader.GetAttribute("Width"));
            LocationX = double.Parse(reader.GetAttribute("XLocation"));
            LocationY = double.Parse(reader.GetAttribute("YLocation"));
           if(!reader.IsEmptyElement)
            {
                while (reader.Read())
                {
                    if (reader.Name.Equals("StoryCard"))
                    {
                        StoryCard child = new StoryCardDataObject();
                        child.ReadXml(reader);
                        AddStoryCard(child);
                    }
                    else if (reader.Name.Equals("Property"))
                    {
                        if (reader.GetAttribute("key").Equals("APDTAngle"))
                            this.rotationAngle = double.Parse(reader.GetAttribute("value"));
                    }
                    else
                        break;
                }
           }
                    
               

            
  

        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("Iteration");
            writer.WriteAttributeString("AvailableEffort", ((float)this.AvailableEffort).ToString());
            writer.WriteAttributeString("Description", this.description);
            writer.WriteAttributeString("EndDate", this.EndDate.ToString("yyyy-MM-dd HH:mm:ss.FFF"));
            writer.WriteAttributeString("Height", ((int)this.Height).ToString());
            writer.WriteAttributeString("ID", this.ID.ToString());
            writer.WriteAttributeString("SyncID", this.SyncID.ToString());
            writer.WriteAttributeString("Name", this.Name);
            writer.WriteAttributeString("Parent", this.Parent.ToString());
            writer.WriteAttributeString("StartDate", this.StartDate.ToString("yyyy-MM-dd HH:mm:ss.FFF"));
            writer.WriteAttributeString("Status", this.Status);
            writer.WriteAttributeString("Width", ((int)this.Width).ToString());
            writer.WriteAttributeString("XLocation", ((int)this.LocationX).ToString());
            writer.WriteAttributeString("YLocation", ((int)this.LocationY).ToString());

            if (StoryCardChildren.Count > 0)
            {
                foreach (StoryCard sc in StoryCardChildren)
                {
                    sc.WriteXml(writer);
                }
            }

            //write rotation Angle Property
            writer.WriteStartElement("Property");
            writer.WriteAttributeString("key", "APDTAngle");
            writer.WriteAttributeString("value", this.rotationAngle.ToString());
            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        #endregion

        #region ICloneable Members

        public override object Clone()
        {
            return new IterationDataObject(this);
        }

        #endregion
    }
}